package cc.shacocloud.redis.study.util.redis;

import cc.shacocloud.redis.study.util.codeBlock.CodeBlockMD;
import cc.shacocloud.redis.study.util.codeBlock.CodeBlockMDManage;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

import java.util.concurrent.TimeUnit;

/**
 * redis 锁操作工具
 */
@Slf4j
@CodeBlockMD(mdDirPath = "/redis/lock", mdFileName = "mutexLock")
public class RedisLockUtil {

    /**
     * 尝试释放锁
     * <p>
     * 逻辑：
     * 如果锁已经不存在， 返回null
     * 如果锁存在，并且锁凭证一致，则删除 key，返回null
     * 如果凭证不一致 则返回锁的过期时间
     *
     * @param lockName             锁的名称
     * @param reentrantCertificate 可重入状态凭证
     * @return 执行结果 {@linkplain RLock}
     */
    @NotNull
    @Contract("_, _ -> new")
    public static RLock tryUnlock(@NotNull String lockName, @NotNull String reentrantCertificate) {
        String unlock = CodeBlockMDManage.get(RedisLockUtil.class, "unlock");
        final Object evalResult = RedisUtil.eval(unlock, 1, lockName, reentrantCertificate);
        final boolean lockStatus = evalResult == null;
        if (lockStatus) return new RLock(lockName, reentrantCertificate, 0, TimeUnit.MILLISECONDS, true);
        else
            return new RLock(lockName, reentrantCertificate, Convert.convert(Long.class, evalResult), TimeUnit.MILLISECONDS, false);
    }

    /**
     * 尝试获取锁
     * <p>
     * 锁凭证使用 {@linkplain IdUtil#objectId()}生成
     *
     * @param lockName          锁的名称
     * @param leaseTimeByMillis 过期时间 单位/毫秒
     * @return 执行结果 {@linkplain RLock}
     */
    @NotNull
    public static RLock tryLock(@NotNull String lockName, long leaseTimeByMillis) {
        return tryLock(lockName, IdUtil.objectId(), leaseTimeByMillis, TimeUnit.MILLISECONDS);
    }

    /**
     * 尝试获取锁
     * <p>
     * 锁凭证使用 {@linkplain IdUtil#objectId()}生成
     *
     * @param lockName  锁的名称
     * @param leaseTime 过期时间
     * @param unit      过期时间单位
     * @return 执行结果 {@linkplain RLock}
     */
    @NotNull
    public static RLock tryLock(@NotNull String lockName, long leaseTime, @NotNull TimeUnit unit) {
        return tryLock(lockName, IdUtil.objectId(), leaseTime, unit);
    }

    /**
     * 尝试获取锁
     *
     * @param lockName             锁的名称
     * @param reentrantCertificate 可重入状态凭证
     * @param leaseTimeByMillis    过期时间 单位/毫秒
     * @return 执行结果 {@linkplain RLock}
     */
    @NotNull
    public static RLock tryLock(@NotNull String lockName, @NotNull String reentrantCertificate, long leaseTimeByMillis) {
        return tryLock(lockName, reentrantCertificate, leaseTimeByMillis, TimeUnit.MILLISECONDS);
    }

    /**
     * 尝试获取锁
     * <p>
     * 逻辑：
     * 如果锁不存在，则尝试获取并且指定过期时间，
     * 如果锁已存在，并且锁的凭证一致则刷新过期时间，如果凭证不一致，则返回过期时间
     *
     * @param lockName             锁的名称
     * @param reentrantCertificate 可重入状态凭证
     * @param leaseTime            过期时间
     * @param unit                 过期时间单位
     * @return 执行结果 {@linkplain RLock}
     */
    @NotNull
    @Contract("_, _, _, _ -> new")
    public static RLock tryLock(@NotNull String lockName, @NotNull String reentrantCertificate, long leaseTime, @NotNull TimeUnit unit) {
        String lock = CodeBlockMDManage.get(RedisLockUtil.class, "lock");
        //过期时间
        final String internalLockLeaseTime = String.valueOf(unit.toMillis(leaseTime));
        final Object evalResult = RedisUtil.eval(lock, 1, lockName, internalLockLeaseTime, reentrantCertificate);

        final boolean lockStatus = evalResult == null;
        if (lockStatus) return new RLock(lockName, reentrantCertificate, leaseTime, unit, true);
        else
            return new RLock(lockName, reentrantCertificate, Convert.convert(Long.class, evalResult), TimeUnit.MILLISECONDS, false);
    }
}
